#!/usr/bin/env python3

import os,traceback,time
from eeprom import Programmer
from tkinter import *
from tkinter import messagebox, filedialog
from tkinter.ttk import *


class Progressbox(Toplevel):
    def __init__(self, root = None, title = '稍等!',activity='执行中 ...', value = 0):
        Toplevel.__init__(self, root)
        self.__step = IntVar()
        self.__step.set(value)
        self.__act = StringVar()
        self.__act.set(activity)
        self.title(title)
        self.resizable(width=False, height=False)
        self.transient(root)
        self.grab_set()
        Label(self, textvariable = self.__act).pack(padx = 20, pady = 10)
        Progressbar(self, orient = HORIZONTAL, length = 200, 
                variable = self.__step, mode = 'determinate').pack(
                padx = 10, pady = 10)
        self.update_idletasks()

    def setactivity(self, activity):
        self.__act.set(activity)
        self.update_idletasks()

    def setvalue(self, value):
        self.__step.set(value)
        self.update_idletasks()


# 根据读取长度判断芯片名字
def getChipName(endAddr):
    if endAddr==0x1fff:
        return 2864
    elif endAddr==0x7fff:
        return 28256
    else:
        return 2864

def showContent():
    print("读取芯片")
    eeprom = Programmer()
    if not eeprom.is_open:
        messagebox.showerror('错误', '未找到编程器!')
        return

    contentWindow = Toplevel(mainWindow)
    contentWindow.title('EEPROM memory content')
    contentWindow.minsize(200, 100)
    contentWindow.resizable(width=False, height=True)
    contentWindow.transient(mainWindow)
    contentWindow.grab_set()

    l = Listbox(contentWindow, font = 'TkFixedFont', height = 36, width = 72)
    l.pack(side='left', fill=BOTH)
    s = Scrollbar(contentWindow, orient = VERTICAL, command = l.yview)
    l['yscrollcommand'] = s.set
    s.pack(side='right', fill='y')

    startAddr = 0
    endAddr = eepromType.get()
    eeprom.sendcommand ('r', getChipName(endAddr), startAddr, endAddr)
    while (startAddr < endAddr):
        bytesline = '%04x:  ' % startAddr
        asciiline = ' '
        i = 16
        while(i):
            data = eeprom.read(1)
            if data:
                bytesline += '%02x ' % data[0]
                if (data[0] > 31) and data[0] < 127: asciiline += chr(data[0])
                else: asciiline += '.'
                i -= 1
        l.insert('end', bytesline + asciiline)
        startAddr += 16
    # eeprom.readline()
    eeprom.close()

    contentWindow.mainloop()
    contentWindow.quit()



def uploadBin():
    print("上传")
    # if eepromType == 0xffff:
    #     messagebox.showerror('Error', 'W27C256不支持写操作')
    #     return
    eeprom = Programmer()
    if not eeprom.is_open:
        messagebox.showerror('错误', '未找到编程器!')
        return

    maxAddr = eepromType.get()
    startAddr = 0

    fileName = filedialog.askopenfilename(title = "选择要写入的文件",
                filetypes = (("binary files","*.bin"),("all files","*.*")))
    if not fileName:
        return

    progress = Progressbox(mainWindow, '正在写入 ...')

    try:
        fileSize = os.stat(fileName).st_size
        if (startAddr + fileSize) > (maxAddr + 1):
            messagebox.showerror('错误', '所选文件与芯片容量不符!')
            eeprom.close()
            return

        # 解锁
        eeprom.unlock()

        totalCount = 0
        address = 0
        buffer_size = 64
        writeDataLength = fileSize
        # 写入是否出错
        writeHasError = False
        with  open(fileName, 'rb') as f:
            while writeDataLength > 0 and not writeHasError:
                # 读取缓存长度的数据
                buffer_data = f.read(buffer_size)

                end = address + buffer_size - 1
                print('{}-{}'.format(('%04X' % address), ('%04X' % end)))

                # sleep(0.04)
                eeprom.sendcommand("w", getChipName(fileSize), address, end)
                # 与程序设置延时时间一致后再发送数据
                time.sleep(0.01)
                eeprom.write(buffer_data)

                address += buffer_size
                writeDataLength -= buffer_size
                totalCount += buffer_size

                # self.progressBarChange(int(totalCount * 100 / filelength))

                reErrorCount = 0
                while True and not writeHasError:
                    try:
                        line = eeprom.getline()
                        if line.find("error") > -1:
                            writeHasError = True
                            break
                        elif line.find('ok') > -1:
                            print(line)
                            break
                        elif len(line) == 0 or line == '':
                            reErrorCount += 1
                            # 2秒超时 尝试10次
                            if reErrorCount > 10:
                                writeHasError = True
                                print("连接超时")
                                break
                        else:
                            print("未知错误 {}".format(line))
                            break
                    except Exception as e:
                        print("错误")
                        print(e)
                        traceback.print_exc()



            # datalength = fileSize
            # byteswritten = 0
            # addr1 = startAddr
            # while (datalength):
            #     count = (addr1 | 0x3f) - addr1 + 1
            #     if count > datalength:
            #         count = datalength
            #     addr2 = addr1 + count - 1
            #     eeprom.sendcommand('w', getChipName(addr2), addr1, addr2)
            #     eeprom.write(f.read(count))
            #     byteswritten += count
            #     progress.setvalue(byteswritten * 100 // fileSize)
            #     redata = eeprom.readline()
            #     print(redata)
            #     addr1 += count
            #     datalength -= count



            print("写入完成")

            eeprom.lock()
            print("加锁完成")
    except Exception as e:
        messagebox.showerror('错误', '打开文件失败!')
        print(e)
        traceback.print_exc()
        return



    try:
        startAddr = 0
        with  open(fileName, 'rb') as f:
            progress.setactivity('校验写入数据...')
            endAddr = fileSize - 1
            count = fileSize
            eeprom.sendcommand('r', getChipName(endAddr), startAddr, endAddr)

            while (count>0):
                # 字符
                data = eeprom.read(1)
                data = data[0]
                if len(str(data)) > 0:
                    # 数字
                    readByte = f.read(1)
                    readByte = int.from_bytes(readByte,byteorder='little', signed=True)
                    readByte = (readByte+256) if readByte<0 else readByte;
                    if data != readByte:
                        messagebox.showerror('错误', '校验错误!')
                        print("位置 {}".format(count))
                        print("数据 {}".format(data))
                        print("文件 {}".format(readByte))
                        eeprom.close()
                        return
                    count -= 1
            # eeprom.readline()

            eeprom.close()
            messagebox.showinfo('任务完成', str(fileSize) + ' 字节写入成功 !')

    except Exception as e:
        print(e)
        traceback.print_exc()
        messagebox.showerror('错误', '校验错误!')
        eeprom.close()
        return
    progress.destroy()


def downloadBin():
    print("下载到电脑")
    eeprom = Programmer()
    if not eeprom.is_open:
        messagebox.showerror('错误', '未找到编程器!')
        return

    startAddr = 0
    endAddr   = eepromType.get()
    count     = endAddr - startAddr + 1

    fileName = filedialog.asksaveasfilename(title = "输出文件",
                filetypes = (("binary files","*.bin"),("all files","*.*")))
    if not fileName:
        return

    # 补充bin扩展名
    if not fileName.lower().endswith(".bin"):
        fileName = fileName+".bin"

    try:
        f = open(fileName, 'wb')
    except:
        messagebox.showerror('错误', '无法打开文件!')
        eeprom.close()
        return


    eeprom.sendcommand ('r', getChipName(endAddr), startAddr, endAddr)
    while (count):
        data = eeprom.read(1)
        if data:
            f.write(data)
            count -= 1
    eeprom.readline()
    f.close()
    eeprom.close()
    messagebox.showinfo("提示",'下载完成!')

def lock():
    # if eepromType == 0xffff:
    #     messagebox.showerror('Error', 'W27C256没有锁')
    #     return
    eeprom = Programmer()
    if not eeprom.is_open:
        messagebox.showerror('错误', '未找到编程器!')
        return
    eeprom.unlock()
    eeprom.close()
    messagebox.showinfo('lock', 'lock !')
def unlock():
    # if eepromType == 0xffff:
    #     messagebox.showerror('Error', 'W27C256没有锁')
    #     return
    eeprom = Programmer()
    if not eeprom.is_open:
        messagebox.showerror('错误', '未找到编程器!')
        return
    eeprom.unlock()
    eeprom.close()
    messagebox.showinfo('unlock', 'unlock !')



if __name__ == '__main__':
    mainWindow = Tk()
    mainWindow.title('28编程器')
    mainWindow.geometry("300x300+400+200")
    mainWindow.resizable(width=False, height=False)

    eepromType = IntVar()
    eepromType.set(0x1fff)

    typeFrame = Frame(mainWindow, borderwidth = 2, relief = 'groove')
    Label(typeFrame, text = '1. 选择芯片类型:').pack(pady = 5)
    # Radiobutton(typeFrame, text = '28C64B ( 1k)', variable=eepromType, value = 0x03ff).pack()# 二进制13
    Radiobutton(typeFrame, text = '28C64B   ( 8k)', variable=eepromType, value = 0x1fff).pack()# 二进制13
    # Radiobutton(typeFrame, text = '28C128  (16k)', variable=eepromType, value = 0x3fff).pack()# 二进制14
    Radiobutton(typeFrame, text = '28C256   (32k)', variable=eepromType, value = 0x7fff).pack()# 二进制15
    # bt512 = Radiobutton(typeFrame, text = 'W27C512(64k)', variable=eepromType, value = 0xffff).pack()# 二进制16
    typeFrame.pack(padx = 10, pady = 10, ipadx = 5, ipady = 5, fill = 'x')

    actionFrame = Frame(mainWindow, borderwidth = 2, relief = 'groove')
    Label(actionFrame, text = '2. 执行命令:').pack(pady = 5)
    Button(actionFrame, text = '读取', command = showContent).pack(padx = 10, fill = 'x')
    Button(actionFrame, text = '写入到芯片', command = uploadBin).pack(padx = 10, fill = 'x')
    Button(actionFrame, text = '读取芯片到文件', command = downloadBin).pack(padx = 10, fill = 'x')

    # Button(actionFrame, text = 'Lock', command = lock,width = 8).pack()
    # Button(actionFrame, text = 'Unlock', command = unlock,width = 8).pack()

    actionFrame.pack(padx =10, ipadx = 5, ipady = 5, fill = 'x')

    Button(mainWindow, text = '退出', command = mainWindow.quit).pack(pady = 10)

    mainWindow.mainloop()
